/*! \file pres_parser.yc
 *
 * \brief bison file describing the grammar for the inspector templates.
 *
 * \date Started: 5/17/2010
 * \date Last Modified: 6/28/2010
 * \authors Michelle Strout, Alan LaMielle, Nicholas Jeanette
 *
 * Copyright (c) 2010, Colorado State University <br>
 * All rights reserved. <br>
 * See ../../COPYING for details. <br>
*/


%{
   extern int yylex ();
   #include <iegenlib/iegenlib.h>
   #include <iostream>
   #include <list>
   #include <stack>
   #define YYERROR_VERBOSE
   #define YYDEBUG 1

   //Required for yyerror below
   void yyerror(const char*);
   extern char *yytext;
   extern int yylineno;

   using namespace iegenlib;

   //Required for access to getter/setter for parse set result
   extern PresSet pres_parser::get_parse_set_result();
   extern void pres_parser::set_parse_set_result(PresSet* s);

   //Required for access to getter/setter for parse relation result
   extern PresRelation pres_parser::get_parse_relation_result();
   extern void pres_parser::set_parse_relation_result(PresRelation* s);

   //Required to get error flag
   extern void pres_parser::set_parse_error(string err);

%}

%union {
   std::string*                  sval;
   int                           ival;
   iegenlib::VarExp*             vexp;
   list<iegenlib::VarExp*>*      vlist;
   iegenlib::VarTuple*           vtup;
   iegenlib::Constraint*         constra;
   iegenlib::Equality*           eql;
   iegenlib::Inequality*         iql;
   list<iegenlib::Constraint*>*  conlist;
   iegenlib::PresSet*            pset;
   iegenlib::PresRelation*       prel;
   iegenlib::NormExp*            nexp;
   list<iegenlib::NormExp*>*     explist;
   list<iegenlib::Conjunction*>* conjlist;
   iegenlib::Conjunction*        conj;
   list<string>*                 symbolist;
   std::string*                  symbolic;
   list<string>*                 existslist;
};

//tokens
%token LBRACE RBRACE
%token LBRACKET RBRACKET
%token LPAREN RPAREN
%token COMMA
%token COLON
%token LT LTE
%token GT GTE
%token SEMI
%token OR
%token UNION
%token EXISTS
//%token NEQ
%token EQ
%token ARROW
%token <sval> ID
%token <ival> INT

//tokens with precedence
%left PLUS DASH
%left STAR
%left UMINUS
%left AND WAND

//Expression
%type <explist> expr_list
%type <nexp> expression_simple
%type <nexp> expression_id
%type <nexp> expression_func
%type <nexp> expression_int_mult
%type <nexp> expression_paren
%type <nexp> expression_unop
%type <nexp> expression_binop
%type <nexp> expression_int
%type <nexp> expression

//Constraint
%type <iql> constraint_lte
%type <iql> constraint_lt
%type <iql> constraint_gte
%type <iql> constraint_gt
//%type <iql> constraint_neq
%type <eql> constraint_eq
%type <constra> constraint
%type <conlist> constraint_list
%type <conlist> optional_constraints

//Variables
%type <sval> variable_id
%type <vexp> tuple_variable
%type <vlist> tuple_variable_list
%type <existslist> exists_id_list
%type <vtup> variable_tuple

//Symbolics

%type <symbolist> InSymbolic_list;
%type <symbolic> InSymbolic;

//Conjunctions
%type <conjlist> omegasetlist
%type <conjlist> semisetlist
%type <conjlist> omegaRellist
%type <conjlist> semiRellist
%type <conjlist> islsetlist
%type <conjlist> islRellist
%type <conj> singleconjunct
%type <conj> singleRelconjunct

//Main
%type <pset> setunionlist
%type <prel> relationunionlist
%start Start

//cleanup after complete
%destructor {} <ival>
%destructor {delete ($$);} <*>



%%
   //the start of the grammar can either take a set or relation
   //set makes a call to set_parse_set_result in pres_parser.cc setting the
   //parsed set. relation makes a call to set_parse_relation_result in
   //pres_parser.cc setting the parsed relation

   Start : setunionlist
                  {pres_parser::set_parse_set_result($1);}

               | relationunionlist
                  {pres_parser::set_parse_relation_result($1);}
   //error is a predefined token for error cases If there is an error this clears
   //out our look ahead token
               | error
                  {yyclearin;}
   ;

    setunionlist: omegasetlist
                  {$$ = new PresSet(*$1);
                   delete $1;}

               | islsetlist
                  {$$ = new PresSet(*$1);
                   delete $1;}

               | LBRACKET InSymbolic_list RBRACKET ARROW omegasetlist
                  {$$ = new PresSet(*$5, $2);
                   delete $5;
                   delete $2;
                  }

               | LBRACKET InSymbolic_list RBRACKET ARROW islsetlist
                 {$$ = new PresSet(*$5, $2);
                   delete $5;
                   delete $2;
                 }
   ;

    singleconjunct: LBRACE variable_tuple optional_constraints RBRACE
                  {$$ =new Conjunction($2, NULL, $3);
                   delete $3;}
    ;

    omegasetlist: singleconjunct
                  {list<Conjunction*>* conlist = new list<Conjunction*>();
                   conlist->push_back($1);
                   $$ = conlist;}

                | omegasetlist UNION singleconjunct
                  {$1->push_back($3);
                   $$=$1;}
    ;

    islsetlist:  LBRACE variable_tuple optional_constraints SEMI semisetlist RBRACE
                 {$5->push_back(new Conjunction($2, NULL, $3));
                  $$ = $5;
                  delete $3;
                 }
    ;

    semisetlist: variable_tuple optional_constraints SEMI semisetlist
                  {$4->push_back(new Conjunction($1, NULL, $2));
                   $$=$4;
                   delete $2;}

                | variable_tuple optional_constraints
                  {list<Conjunction*>* conlist = new list<Conjunction*>();
                   conlist->push_back(new Conjunction($1, NULL, $2));
                   $$ = conlist;
                   delete $2;}
    ;

    relationunionlist: omegaRellist
                  {$$ = new PresRelation(*$1);
                   delete $1;}

                | islRellist
                  {$$ = new PresRelation(*$1);
                   delete $1;}

                | LBRACKET InSymbolic_list RBRACKET ARROW omegaRellist
                  {$$ = new PresRelation(*$5, $2);
                   delete $5;
                   delete $2;
                  }

                | LBRACKET InSymbolic_list RBRACKET ARROW islRellist
                  {$$ = new PresRelation(*$5, $2);
                   delete $5;
                   delete $2;
                  }
    ;

    singleRelconjunct: LBRACE variable_tuple ARROW variable_tuple optional_constraints RBRACE
                  {$$ =new Conjunction($2, $4, $5);
                   delete $5;}
    ;

    omegaRellist: singleRelconjunct
                  {list<Conjunction*>* conlist = new list<Conjunction*>();
                   conlist->push_back($1);
                   $$ = conlist;}

                | omegaRellist UNION singleRelconjunct
                  {$1->push_back($3);
                   $$=$1;}
    ;

    islRellist:  LBRACE variable_tuple ARROW variable_tuple optional_constraints SEMI semiRellist RBRACE
                 {$7->push_back(new Conjunction($2, $4, $5));
                  $$ = $7;
                  delete $5;
                 }
    ;

    semiRellist: variable_tuple ARROW variable_tuple optional_constraints SEMI semiRellist
                  {$6->push_back(new Conjunction($1, $3, $4));
                   $$=$6;
                   delete $4;}

                | variable_tuple ARROW variable_tuple optional_constraints
                  {list<Conjunction*>* conlist = new list<Conjunction*>();
                   conlist->push_back(new Conjunction($1, $3, $4));
                   $$ = conlist;
                    delete $4;}
    ;

   //======== inSymbolics Rules ========\\

   InSymbolic_list: InSymbolic
                    {list<string>* Symblist = new list<string>();
                     Symblist->push_back(*$1);
                     $$ = Symblist;
                     delete $1;
                    }

                  | InSymbolic COMMA InSymbolic_list
                    {$3->push_back(*$1);
                     $$ = $3;
                     delete $1;
                    }
   ;

   InSymbolic: ID
               {$$ = $1;}
   ;

   //======== VARIABLE TUPLE RULES ========\\

   //creates a new VarTuple production with either the tuple list or an empty list
   variable_tuple: LBRACKET tuple_variable_list RBRACKET
                  {$$= new VarTuple($2);
                  delete $2;}

   //
               | LBRACKET tuple_variable_list error
                  {for (list<VarExp*>::iterator it = $2->begin(); it != $2->end(); it++)
                    {
                      delete *it;
                    }
                   delete $2;
                   $$ = NULL;
                   yyclearin;
                  }

               | LBRACKET RBRACKET
                  {$$= new VarTuple();}
   ;

   //creates a new vlist and adds tuple variables to it
   tuple_variable_list: tuple_variable
                  { list<VarExp*>* varlist = new list<VarExp*>();
                  varlist->push_back($1);
                  $$ = varlist;}

               | tuple_variable_list COMMA tuple_variable
                  {$1->push_back($3);
                  $$=$1;}
   ;

   //creates a new VarExp with a variable id or an int
   tuple_variable: variable_id
                  {$$ = new VarExp(1, string(*$1));
                  delete $1;}

               | INT
                  {int a = $1;
                  string blank;
                  $$ = new VarExp(a, blank);}
   ;

   //returns a string of the ID
   variable_id: ID
                  {$$ = $1;}
   ;

   //======== OPTIONAL CONSTRAINTS RULES ========\\

   //creates a new conjunction production with either a conjunction list of
   //an empty list
   optional_constraints: COLON constraint_list
                  {$$ = $2;}

               | COLON EXISTS LPAREN exists_id_list COLON constraint_list RPAREN
                  {$4; $$ = $6;}

   //this rule allows us to grab the constraint list after there has been an error
   //so we can delete the constraints that we created
               | COLON constraint_list error
                  {for (list<Constraint*>::iterator it = $2->begin(); it != $2->end(); it++)
                    {
                        delete *it;
                    }
                   delete $2;
                   $$ = NULL;
                   yyclearin;}

              | //epsilon
                  {$$ = new list<Constraint*>();}
   ;

   exists_id_list: ID
                  {$$=NULL; delete $1;}

               | exists_id_list COMMA ID
                  {$$=NULL; $1; delete $3;}
   ;

   //builds a constraint list. Constraints are continued with the either
   //the word and or &&
   constraint_list: constraint
                  {list<Constraint*>* clist = new list<Constraint*>();
                  clist->push_back($1);
                  $$ = clist;}

               | constraint_list AND constraint
                  //(*$1 + "&&" + *$3)
                  { $1->push_back($3);
                 $$=$1;}

               | constraint_list WAND constraint
                  //(*$1 + "and" + *$3)
                  { $1->push_back($3);
                 $$=$1;}


   ;

   //passes the constraint returned from one of the cases (>,>=,<,<=,=) up
   //note != is not allowed
   constraint: constraint_eq
                  {$$ = $1;}

//               | constraint_neq
//                  {$$ = $1;}

               | constraint_gt
                  {$$ = $1;}

               | constraint_gte
                  {$$ = $1;}

               | constraint_lt
                  {$$ = $1;}

               | constraint_lte
                  {$$ = $1;}

   ;

   //constructs an Equality from an equals expression
   //($1 = $3) = ($1-$3=0)
   constraint_eq: expression EQ expression
                  { $$ = new Equality(new NormExp((*$1)-(*$3)));
                  delete $1; delete $3;}
   ;

   //constructs an Inequality form a not equals expression
   //NOTE NOT implemented!
//    constraint_neq: expression NEQ expression
//                  // ($1!=$3) = ($3>$1 && $1>$3)
//                  //= ($3>=$1+1 && $1>=$3+1)
//                  //raise Exception('This should be || and not &&!  Need to fix!')
//                  {$$ = new Inequality($3-($1+NormExp(list<Expression*>(),1))),
//                      new Inequality($1-($3+1));}
//   ;

   //constructs an Inequality from a greater than expression
   //($1 > $3) = ($1 >= $3+1) = ($1-($3+1) >= 0)
   constraint_gt:expression GT expression
                  {  $$= new Inequality(new NormExp((*$1)-((*$3)+NormExp(1))));
                  delete $1; delete $3;}
   ;

   //constructs an Inequality from a greater than equals expression
   //($1 >= $3) = ($1-$3 >= 0)
   constraint_gte: expression GTE expression
                  {   ;$$ = new Inequality(new NormExp((*$1)-(*$3)));
                  delete $1; delete $3;}
   ;

   //constructs an Inequality from a less than expression
   //($1 < $3) = ($3 > $1) = ($3 >= $1+1) = ($3-($1+1) >= 0)
   constraint_lt: expression LT expression
                  {  $$ = new Inequality(new NormExp((*$3)-((*$1)+NormExp(1))));
                  delete $1; delete $3;}
   ;

   //constructs an Inequality from a less than equals expression
   //($1 <= $3) = ($3 >= $1) = ($3-$1 >= 0)
   constraint_lte: expression LTE expression //returns Inequality
                  {  $$= new Inequality(new NormExp((*$3)-(*$1)));
                  delete $1; delete $3;}
   ;

   //======== EXPRESSION PRODUCTIONS ===========\\

   //passes expressions up, each expression is in terms of a NormExp
   expression: expression_int
                  {$$ = $1;}
               | expression_unop
                  {$$ = $1;}

               | expression_binop
                  {$$ = $1;}

               | expression_int_mult
                  {$$ = $1;}

               | expression_simple
                  {$$ = $1;}
   ;

   //creates a new NormExp from the integer
   expression_int: INT
                  {$$ = new NormExp($1);}
   ;

   //preforms the unary minus on the expression creating a new NormExp
   //Note the precedence is the same as UMINUS
   expression_unop: DASH expression %prec UMINUS
                  {$$= new NormExp(-(*$2));
                  delete $2;}
   ;

   //preforms binary operators +,-,* on two NormExp creating a new NormExp
   expression_binop: expression PLUS expression
                  {$$ = new NormExp((*$1)+(*$3));
                  delete $1; delete $3;}

               | expression DASH expression
                  {$$ = new NormExp((*$1)-(*$3));
                  delete $1; delete $3;}

               | expression STAR expression
                  {$$ = new NormExp((*$1) * (*$3));
                  delete $1; delete $3;}
   ;

   //performs a integer multiplication on a simple expression
   //eg 4(x), 4x, 4f(x)
   expression_int_mult: INT expression_simple
                  {NormExp tmp($1);
                  $$= new NormExp( (tmp)  * (*$2));
                  delete $2;}
   ;

   //passes the NormExp up
   expression_simple: expression_id
                  {$$ = $1;}

               | expression_func
                  {$$ = $1;}

               | expression_paren
                  {$$ = $1;}
   ;

   //creates a new NormExp from the ID
   expression_id: ID
                  {list<Expression*> l;
                  l.push_back(new VarExp(1, *$1));
                  $$ = new NormExp(&l,0);
                  delete $1;}
   ;

   //creates an expression function and makes a NormExp from that to keep with
   //all expressions returning a NormExp
   //eg f(x)
   expression_func: variable_id LPAREN expr_list RPAREN
                  {list<Expression*> flist;
                  (flist).push_back( new FuncExp(1,string(*$1),($3)));
                  $$ = new NormExp((&flist),0);
                  delete $1; delete $3;}

   //this rule allows us to grab the expr list after there has been an error
   //so we can delete the normExp that we created
               | variable_id LPAREN expr_list error
                  {for (list<NormExp*>::iterator it = $3->begin(); it != $3->end(); it++)
                     {
                        delete *it;
                     }
                   $$ = NULL;
                   delete $1; delete $3;
                   yyclearin;}
   ;

   //returns an expression enclosed with parens
   expression_paren: LPAREN expression RPAREN
                  {$$=$2;}
   ;

   //builds a list of expressions for use in building a function expression
   //given a single expression a new list is made otherwise expressions are
   //increased by the comma eg x-9,4y,y-x
   expr_list:  expr_list COMMA expression //returns list
                  {$1->push_back($3);
                  $$=$1;}

               | expression
                  {list<NormExp*>* elist = new list<NormExp*>();
                  elist->push_back($1);
                  $$ = elist;}
   ;

%%


/*! yyerror is called when an error occurs in parsing. It prints localion of the
error as well as why the error occured to standard error. When an error occurs
we set the parse_error;
@param const char pointer
*/
#include <iostream>
#include <typeinfo>
#include <sstream>
void yyerror (const char *s)
{
   std::stringstream EM;
   EM << s << " at '" << yytext << "', line " << yylineno << endl;
   //sets the parse error flag to true
   pres_parser::set_parse_error(EM.str());
   pres_parser::clearAll();
}

